#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
化学反应演算建模系统
整合化合物数据库查询和反应计算功能
"""

import json
import logging
from pathlib import Path
from typing import Dict, List, Optional
from compound_database import CompoundDatabase, CompoundData
from reaction_calculator import (
    ReactionDesigner, ReactionCalculator, ChemicalReaction,
    ReactionThermodynamics
)

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


class ReactionModelingSystem:
    """化学反应演算建模系统"""
    
    def __init__(self, data_dir: str = "data"):
        """
        初始化系统
        
        Args:
            data_dir: 数据目录
        """
        self.data_dir = Path(data_dir)
        self.data_dir.mkdir(parents=True, exist_ok=True)
        
        # 初始化子系统
        self.compound_db = CompoundDatabase(cache_dir=str(self.data_dir / "compounds"))
        self.reaction_designer = ReactionDesigner(compound_db=self.compound_db)
        self.calculator = ReactionCalculator()
        
        # 反应库
        self.reactions: Dict[str, ChemicalReaction] = {}
        
        logger.info("化学反应演算建模系统已初始化")
    
    def query_compound(self, identifier: str, search_type: str = "name") -> Optional[CompoundData]:
        """
        查询化合物数据
        
        Args:
            identifier: 化合物标识符（名称或CAS号）
            search_type: 搜索类型 ("name" 或 "cas")
            
        Returns:
            化合物数据
        """
        if search_type == "name":
            return self.compound_db.search_by_name(identifier)
        elif search_type == "cas":
            return self.compound_db.search_by_cas(identifier)
        else:
            logger.error(f"不支持的搜索类型: {search_type}")
            return None
    
    def batch_query_compounds(self, identifiers: List[str], 
                             search_type: str = "name") -> List[CompoundData]:
        """批量查询化合物"""
        return self.compound_db.batch_search(identifiers, search_type)
    
    def design_reaction(self,
                       reaction_id: str,
                       reaction_name: str,
                       equation: str,
                       query_compounds: bool = True,
                       thermodynamics: Dict = None,
                       **kwargs) -> ChemicalReaction:
        """
        设计化学反应
        
        Args:
            reaction_id: 反应ID
            reaction_name: 反应名称
            equation: 化学方程式
            query_compounds: 是否自动查询化合物数据
            thermodynamics: 热力学数据
            **kwargs: 其他参数
            
        Returns:
            化学反应对象
        """
        compound_data = {}
        
        if query_compounds:
            # 从方程式中提取化合物
            from reaction_calculator import ReactionParser
            parser = ReactionParser()
            reactants, products = parser.parse_equation(equation)
            
            all_compounds = reactants + products
            
            # 查询每个化合物
            for comp in all_compounds:
                formula = comp['formula']
                
                # 尝试通过分子式查询
                compound = self.compound_db.search_by_name(formula)
                
                if compound:
                    compound_data[formula] = {
                        'name': compound.name,
                        'molecular_weight': compound.molecular_weight,
                        'smiles': compound.smiles,
                        'cas_number': compound.cas_number
                    }
                    logger.info(f"已查询化合物: {formula} -> {compound.name}")
        
        # 创建反应
        reaction = self.reaction_designer.create_reaction(
            reaction_id=reaction_id,
            reaction_name=reaction_name,
            equation=equation,
            compound_data=compound_data,
            thermodynamics=thermodynamics,
            **kwargs
        )
        
        # 保存到反应库
        self.reactions[reaction_id] = reaction
        
        logger.info(f"已创建反应: {reaction_id} - {reaction_name}")
        
        return reaction
    
    def calculate_material_balance(self,
                                   reaction_id: str,
                                   reactant_masses: Dict[str, float],
                                   yield_factor: float = 1.0) -> Dict:
        """
        计算物料平衡
        
        Args:
            reaction_id: 反应ID
            reactant_masses: 反应物质量 {formula: mass_kg}
            yield_factor: 收率系数 (0-1)
            
        Returns:
            物料平衡结果
        """
        if reaction_id not in self.reactions:
            logger.error(f"反应不存在: {reaction_id}")
            return {}
        
        reaction = self.reactions[reaction_id]
        
        # 计算理论产量
        theoretical_products = self.calculator.balance_mass(reaction, reactant_masses)
        
        # 应用收率系数
        actual_products = {
            formula: mass * yield_factor 
            for formula, mass in theoretical_products.items()
        }
        
        result = {
            'reaction_id': reaction_id,
            'reaction_name': reaction.reaction_name,
            'reactants': reactant_masses,
            'theoretical_products': theoretical_products,
            'actual_products': actual_products,
            'yield_factor': yield_factor
        }
        
        return result
    
    def analyze_thermodynamics(self, reaction_id: str, temperature: float = 298.15) -> Dict:
        """
        分析反应热力学
        
        Args:
            reaction_id: 反应ID
            temperature: 温度 (K)
            
        Returns:
            热力学分析结果
        """
        if reaction_id not in self.reactions:
            logger.error(f"反应不存在: {reaction_id}")
            return {}
        
        reaction = self.reactions[reaction_id]
        
        if not reaction.thermodynamics:
            logger.warning(f"反应 {reaction_id} 缺少热力学数据")
            return {}
        
        thermo = reaction.thermodynamics
        
        # 计算平衡常数
        K = None
        if thermo.delta_G is not None:
            K = self.calculator.calculate_equilibrium_constant(
                thermo.delta_G, temperature
            )
        
        # 判断自发性
        spontaneous = None
        if thermo.delta_G is not None:
            spontaneous = self.calculator.is_spontaneous(thermo.delta_G)
        
        result = {
            'reaction_id': reaction_id,
            'reaction_name': reaction.reaction_name,
            'temperature': temperature,
            'delta_H': thermo.delta_H,
            'delta_G': thermo.delta_G,
            'delta_S': thermo.delta_S,
            'equilibrium_constant': K,
            'is_spontaneous': spontaneous,
            'reaction_type': '放热反应' if thermo.delta_H and thermo.delta_H < 0 else '吸热反应'
        }
        
        return result
    
    def generate_process_report(self, reaction_id: str, 
                               reactant_masses: Dict[str, float],
                               yield_factor: float = 0.85,
                               temperature: float = 298.15) -> str:
        """
        生成工艺报告
        
        Args:
            reaction_id: 反应ID
            reactant_masses: 反应物质量
            yield_factor: 收率系数
            temperature: 温度
            
        Returns:
            报告文本
        """
        if reaction_id not in self.reactions:
            return f"错误: 反应 {reaction_id} 不存在"
        
        reaction = self.reactions[reaction_id]
        
        report = f"""
{'='*80}
化学反应工艺报告
{'='*80}

反应ID: {reaction.reaction_id}
反应名称: {reaction.reaction_name}
反应方程式: {reaction.reaction_equation}
反应类型: {reaction.reaction_type or '未指定'}
催化剂: {reaction.catalyst or '无'}

{'='*80}
反应物信息:
{'='*80}
"""
        
        for r in reaction.reactants:
            report += f"  {r.name} ({r.formula})\n"
            report += f"    系数: {r.coefficient}\n"
            report += f"    分子量: {r.molecular_weight} g/mol\n"
            if r.formula in reactant_masses:
                report += f"    投料量: {reactant_masses[r.formula]} kg\n"
            report += "\n"
        
        report += f"""{'='*80}
产物信息:
{'='*80}
"""
        
        for p in reaction.products:
            report += f"  {p.name} ({p.formula})\n"
            report += f"    系数: {p.coefficient}\n"
            report += f"    分子量: {p.molecular_weight} g/mol\n\n"
        
        # 物料平衡
        material_balance = self.calculate_material_balance(
            reaction_id, reactant_masses, yield_factor
        )
        
        report += f"""{'='*80}
物料平衡:
{'='*80}
收率系数: {yield_factor * 100:.1f}%

理论产量:
"""
        for formula, mass in material_balance['theoretical_products'].items():
            report += f"  {formula}: {mass:.3f} kg\n"
        
        report += "\n实际产量:\n"
        for formula, mass in material_balance['actual_products'].items():
            report += f"  {formula}: {mass:.3f} kg\n"
        
        # 热力学分析
        if reaction.thermodynamics:
            thermo_analysis = self.analyze_thermodynamics(reaction_id, temperature)
            
            report += f"""
{'='*80}
热力学分析:
{'='*80}
温度: {temperature} K ({temperature - 273.15:.1f}°C)
焓变 (ΔH): {thermo_analysis.get('delta_H', 'N/A')} kJ/mol
吉布斯自由能 (ΔG): {thermo_analysis.get('delta_G', 'N/A')} kJ/mol
熵变 (ΔS): {thermo_analysis.get('delta_S', 'N/A')} J/(mol·K)
平衡常数 (K): {thermo_analysis.get('equilibrium_constant', 'N/A')}
反应类型: {thermo_analysis.get('reaction_type', 'N/A')}
自发性: {'是' if thermo_analysis.get('is_spontaneous') else '否'}
"""
        
        report += f"\n{'='*80}\n报告生成完成\n{'='*80}\n"
        
        return report
    
    def export_reactions(self, output_file: str):
        """导出所有反应到JSON文件"""
        data = {
            'total_reactions': len(self.reactions),
            'reactions': {
                rid: reaction.to_dict() 
                for rid, reaction in self.reactions.items()
            }
        }
        
        output_path = Path(output_file)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=2)
        
        logger.info(f"已导出 {len(self.reactions)} 个反应到 {output_file}")
    
    def save_report(self, report: str, output_file: str):
        """保存报告到文件"""
        output_path = Path(output_file)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(report)
        
        logger.info(f"报告已保存到: {output_file}")

